#include "hnetdeviceprivate.h"

#include <QDebug>

#include <sys/socket.h>
#include <netinet/in.h>
#include <pcap.h>
#include <netdb.h>


HNetDevicePrivate::HNetDevicePrivate()
{

}

HNetDevicePrivate::~HNetDevicePrivate()
{

}

void HNetDevicePrivate::freeAllDevs()
{
    if (m_allNetDevice) {
        pcap_freealldevs(m_allNetDevice);
        m_allNetDevice = nullptr;
    }
}

bool HNetDevicePrivate::findAllDevs()
{
    freeAllDevs();

    if (pcap_findalldevs(&m_allNetDevice, m_errBuffer) == -1) {
        return false;
    }
    m_deviceCount = 0;
    for (pcap_if_t *index = m_allNetDevice; index != nullptr; index = index->next) {
        m_deviceCount++;
    }

    return true;
}


bool HNetDevicePrivate::openDevice(char *szDevName, int flag, int iLengthLimit)
{
    if (m_currentDevice != nullptr) {
        closeDevice();
    }
    m_currentDevice = pcap_open_live(  szDevName,			// 设备名
                                iLengthLimit,		// 数据包大小限制
                                flag,				// 网卡设置打开模式
                                1000,				// 读取超时时间
                                m_errBuffer);			// 错误缓冲
    if (m_currentDevice == nullptr) {
        return false;
    }

    return true;
}

bool HNetDevicePrivate::openDevice(int devIndex, int flag, int iLengthLimit)
{
    if (devIndex < 1 || devIndex > m_deviceCount) {
        return false;
    }

    pcap_if_t *index = m_allNetDevice;

    for (int i = 1; i < devIndex; i++) {
        index = index->next;
    }

    if (m_currentDevice != nullptr) {
        closeDevice();
    }

    m_currentDevice = pcap_open_live(  index->name,		// 设备名
                                iLengthLimit,		// 数据包大小限制
                                flag,				// 网卡设置打开模式
                                1000,				// 读取超时时间
                                m_errBuffer);			// 错误缓冲

    if (m_currentDevice == nullptr) {
        return false;
    }

    return true;
}

bool HNetDevicePrivate::closeDevice()
{
    if (m_currentDevice != nullptr) {
        pcap_close(m_currentDevice);
        m_currentDevice = nullptr;
        return true;
    }

    return false;
}


bool HNetDevicePrivate::setDeviceFilter(const char *szFilter)
{
    // 检查数据链路层，只考虑以太网
    if ( pcap_datalink(m_currentDevice) != DLT_EN10MB ) {
        return false;
    }

    u_int netmask = 0xFFFFFF;

    struct bpf_program fcode;

    // 布尔表达式转换过滤引擎能识别的字节码
    if (pcap_compile(m_currentDevice, &fcode, szFilter, 1, netmask) < 0) {
        return false;
    }
    if (pcap_setfilter(m_currentDevice, &fcode) < 0) {
        return false;
    }

    return true;
}


bool HNetDevicePrivate::openDumpFile(const QString& fileName)
{
    if (m_dumpFile != nullptr) {
        closeDumpFile();
    }

    if ((m_dumpFile = pcap_dump_open(m_currentDevice, fileName.toStdString().c_str())) != nullptr) {
        return true;
    }

    return false;
}

void HNetDevicePrivate::closeDumpFile()
{
    if (m_dumpFile != nullptr) {
        pcap_dump_close(m_dumpFile);
        m_dumpFile = nullptr;
    }
}



int HNetDevicePrivate::captureOnce()
{
    int res = pcap_next_ex(m_currentDevice, &m_header, &m_pktData);

    if (m_dumpFile != nullptr) {
        saveCaptureData();
    }

    return res;
}

bool HNetDevicePrivate::captureByCallBack(pcap_handler func)
{
    if (m_currentDevice != nullptr) {
        pcap_loop(m_currentDevice, 0, func, (quint8 *)m_dumpFile);
        return true;
    }

    return false;
}


void HNetDevicePrivate::saveCaptureData(u_char *dumpfile, struct pcap_pkthdr *header, u_char *pkt_data)
{
    if (dumpfile != nullptr) {
        pcap_dump(dumpfile, header, pkt_data);
    }
}



void HNetDevicePrivate::saveCaptureData()
{
    if (m_dumpFile != nullptr) {
        pcap_dump((unsigned char *)m_dumpFile, m_header, m_pktData);
    }
}



QString HNetDevicePrivate::ipToString(quint32 ipaddr)
{
    QString ipaddr_str;
    long mask[] = {0x000000FF,0x0000FF00,0x00FF0000,0xFF000000};
    long num = 0;

    for(int i = 0;i < 4;i++){
        num = (ipaddr & mask[i])>>(i*8);
        if(i>0) ipaddr_str.insert(0,".");
        ipaddr_str.insert(0,QString::number(num,10));
    }
    return ipaddr_str;
}

quint32 HNetDevicePrivate::stringToIp(const QString &ipaddr_str)
{
    QStringList ips = ipaddr_str.split(QRegExp("[.]"));
    if (ips.length() < 4) return 0;

    quint32 num =  ((ips[0].toUInt()) << 24) + ((ips[1].toUInt()) << 16) + ((ips[2].toUInt()) << 8) + ips[3].toUInt();
    return num;
}

QString HNetDevicePrivate::ip6ToString(sockaddr *sockaddr, char *address, quint32 addrlen)
{
    socklen_t sockaddrlen;
    sockaddrlen = sizeof(struct sockaddr_storage);

    if (getnameinfo(sockaddr, sockaddrlen, address, addrlen, nullptr,  0, NI_NUMERICHOST) != 0) {
        address = nullptr;
    }

    return address;
}

void HNetDevicePrivate::printDevsInfo()
{
    pcap_if_t	*index;
    pcap_addr_t *pAddr;
    char         ip6str[128];
    int	         i = 0;

    for (index = m_allNetDevice; index != nullptr; index = index->next) {
        qInfo() << ++i << QString(index->name);

        if (index->description) {
            qInfo() << index->description;
        } else {
            qInfo() << (" (No description available)\n\n");
        }

        for (pAddr = index->addresses; pAddr != nullptr; pAddr = pAddr->next) {
            qInfo() << "Address Family: #\n" << pAddr->addr->sa_family;

            switch(pAddr->addr->sa_family)
            {
            case AF_INET:
                qInfo() << "\tAddress Family Name: AF_INET(IPV4)";
                if (pAddr->addr) {
                    qInfo() << "\tAddress: " << ipToString(((struct sockaddr_in *)pAddr->addr)->sin_addr.s_addr);
                }
                if (pAddr->netmask) {
                    qInfo() << "\tNetmask: " << ipToString(((struct sockaddr_in *)pAddr->netmask)->sin_addr.s_addr);
                }
                if (pAddr->broadaddr) {
                    qInfo() << "\tBroadcast Address: " << ipToString(((struct sockaddr_in *)pAddr->broadaddr)->sin_addr.s_addr);
                }
                if (pAddr->dstaddr) {
                    qInfo() << "\tDestination Address: " << ipToString(((struct sockaddr_in *)pAddr->dstaddr)->sin_addr.s_addr);
                }
                break;
            case AF_INET6:
                qInfo() << "\tAddress Family Name: AF_INET6(IPV6)";
                if (pAddr->addr) {
                    qInfo() << "\tAddress: " << ip6ToString(pAddr->addr, ip6str, sizeof(ip6str));
                }
                break;
            default:
                qInfo() << "\tAddress Family Name: Unknown";
            }
            qInfo() << ("\n");
        }
    }
}
